﻿
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Data;
using Dapper;
using dotNET.Data;
using dotNET.Domain;
using System.Data.Common;
namespace dotNET.Repository
{
    public class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : Entity, new()
    {
        private IDbConnection conn;
        private IDbTransaction transaction;
        IDbContext context;
        public RepositoryBase(IDbContext dbContext)
        {
            context = dbContext;
            conn = dbContext.Connection;
            transaction = dbContext.Transaction;
        }
        /// <summary>
        /// 执行sql语句
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<int> testExecuteAsync(string sql, object param = null)
        {
            return await conn.ExecuteAsync(sql, param, transaction);
        }

        /// <summary>
        /// 执行sql语句
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<int> ExecuteAsync(string sql, object param = null)
        {
            return await conn.ExecuteAsync(sql, param, transaction);
        }

        /// <summary>
        /// <para>Inserts a row into the database</para>
        /// <para>By default inserts into the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Insert filters out Id column and any columns with the [Key] attribute</para>
        /// <para>Properties marked with attribute [Editable(false)] and complex types are ignored</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns the ID (primary key) of the newly inserted record if it is identity using the defined type, otherwise null</para>
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<int> InsertAsync(TEntity entity)
        {
            return await conn.InsertAsync<long>(entity, transaction);
        }

        /// <summary>
        /// <para>Updates a record or records in the database</para>
        /// <para>By default updates records in the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Updates records where the Id property and properties with the [Key] attribute match those in the database.</para>
        /// <para>Properties marked with attribute [Editable(false)] and complex types are ignored</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns number of rows effected</para>
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<bool> UpdateAsync(TEntity entity)
        {
            return await conn.UpdateAsync(entity, transaction) > 0;
        }

        /// <summary>
        /// <para>Updates a record or records in the database</para>
        /// <para>By default updates records in the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Updates records where the Id property and properties with the [Key] attribute match those in the database.</para>
        /// <para>Properties marked with attribute [Editable(false)] and complex types are ignored</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns number of rows effected</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<bool> UpdateAsync<T>(object entity)
        {
            return await conn.UpdateAsync<TEntity>(entity, transaction) > 0;
        }


        /// <summary>
        /// <para>Deletes a record or records in the database by ID</para>
        /// <para>By default deletes records in the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Deletes records where the Id property and properties with the [Key] attribute match those in the database</para>
        /// <para>The number of records effected</para>
        /// <para>Supports transaction and command timeout</para>
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<bool> DeleteAsync(long id)
        {
            if (await conn.DeleteAsync(new TEntity() { Id = id }, transaction) > 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// <para>Deletes a list of records in the database</para>
        /// <para>By default deletes records in the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Deletes records where that match the where clause</para>
        /// <para>conditions is an SQL where clause ex: "where name='bob'" or "where age>=@Age"</para>
        /// <para>parameters is an anonymous type to pass in named parameter values: new { Age = 15 }</para>
        /// <para>Supports transaction and command timeout</para>
        /// </summary>
        /// <param name="conditions"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<bool> DeleteListAsync(string conditions, object param = null)
        {
            if (await conn.DeleteListAsync<TEntity>(conditions: conditions, parameters: param, transaction: transaction) > 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// <para>Deletes a list of records in the database</para>
        /// <para>By default deletes records in the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Deletes records where that match the where clause</para>
        /// <para>whereConditions is an anonymous type to filter the results ex: new {Category = 1, SubCategory=2}</para>
        /// <para>The number of records effected</para>
        /// <para>Supports transaction and command timeout</para>
        /// </summary>
        /// <param name="conditions"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<bool> DeleteListAsync(object whereConditions)
        {
            if (await conn.DeleteListAsync<TEntity>(whereConditions, transaction) > 0)
            {
                return true;
            }
            return false;
        }



        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>By default filters on the Id column</para>
        /// <para>-Id column name can be overridden by adding an attribute on your primary key property [Key]</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns a single entity by a single id from table T</para>
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<TEntity> GetAsync(long id)
        {
            return await conn.GetAsync<TEntity>(id, transaction);
        }

        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>whereConditions is an anonymous type to filter the results ex: new {Category = 1, SubCategory=2}</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns a list of entities that match where conditions</para>
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<TResult>> GetListAsync<TResult>(object whereConditions = null)
        {
            if (whereConditions == null)
                return await conn.GetListAsync<TResult, TEntity>(new TEntity { }, transaction);
            return await conn.GetListAsync<TResult, TEntity>(whereConditions, transaction);
        }

        public virtual async Task<IEnumerable<TEntity>> GetListAsync(object whereConditions = null)
        {
            return await conn.GetListAsync<TEntity, TEntity>(whereConditions, transaction);
        }

        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>conditions is an SQL where clause and/or order by clause ex: "where name='bob'" or "where age>=@Age"</para>
        /// <para>parameters is an anonymous type to pass in named parameter values: new { Age = 15 }</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns a list of entities that match where conditions</para>
        /// </summary>
        /// <param name="conditions"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<TEntity>> GetListAsync(string conditions, object param = null)
        {
            return await conn.GetListAsync<TEntity>(conditions, param, transaction);
        }

        public virtual async Task<IEnumerable<TResult>> GetListAsync<TResult>(string conditions, object param = null)
        {
            return await conn.GetListAsync<TResult, TEntity>(conditions, param, transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql">select * ....</param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<TResult>> QueryAsync<TResult>(string sql, object param = null)
        {
            return await conn.QueryAsync<TResult>(sql, param, transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<T> QueryFirstOrDefaultAsync<T>(string conditions, object param = null)
        {
            return (await conn.GetListAsync<T, TEntity>(conditions, param, transaction)).FirstOrDefault();
        }

        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>conditions is an SQL where clause ex: "where name='bob'" or "where age>=@Age" - not required </para>
        /// <para>orderby is a column or list of columns to order by ex: "lastname, age desc" - not required - default is by primary key</para>
        /// <para>parameters is an anonymous type to pass in named parameter values: new { Age = 15 }</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>Returns a list of entities that match where conditions</para>
        /// </summary>
        /// <param name="conditions">where sql</param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<TEntity>> GetListPagedAsync(int pageNumber, int rowsPrePage, string conditions, string orderby, object param = null)
        {
            return await conn.GetListPagedAsync<TEntity>(pageNumber, rowsPrePage, conditions, orderby, param, transaction);
        }

        public virtual async Task<IEnumerable<TResult>> GetListPagedAsync<TResult>(int pageNumber, int rowsPrePage, string conditions, string orderby, object param = null)
        {
            return await conn.GetListPagedAsync<TResult, TEntity>(pageNumber, rowsPrePage, conditions, orderby, param, transaction);
        }

        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Returns a number of records entity by a single id from table T</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>conditions is an SQL where clause ex: "where name='bob'" or "where age>=@Age" - not required </para>
        /// <para>parameters is an anonymous type to pass in named parameter values: new { Age = 15 }</para>
        /// </summary>
        /// <param name="conditions">where sql</param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<int> RecordCountAsync(string conditions = "", object param = null)
        {
            return await conn.RecordCountAsync<TEntity>(conditions: conditions, parameters: param, transaction: transaction);
        }

        /// <summary>
        /// <para>By default queries the table matching the class name</para>
        /// <para>-Table name can be overridden by adding an attribute on your class [Table("YourTableName")]</para>
        /// <para>Returns a number of records entity by a single id from table T</para>
        /// <para>Supports transaction and command timeout</para>
        /// <para>whereConditions is an anonymous type to filter the results ex: new {Category = 1, SubCategory=2}</para>
        /// </summary>
        /// <param name="whereConditions">where</param>
        /// <returns></returns>
        public virtual async Task<int> RecordCountAsync(object whereConditions = null)
        {
            return await conn.RecordCountAsync<TEntity>(whereConditions: whereConditions, transaction: transaction);
        }
    }
}
